#include <jni.h>
#include <string>

extern "C"
JNIEXPORT void JNICALL
Java_com_avic_cplustest_MainActivity_callSuperInstanceMethod(JNIEnv *env, jclass type) {
    jclass cls_cat;
    jclass cls_animal;
    jmethodID mid_cat_init;
    jmethodID mid_run;
    jmethodID mid_getName;
    jstring c_str_name;
    jobject obj_cat;
    const char *name = NULL;

    // 1.获取Cat类的class引用
    cls_cat = (*env).FindClass("com/avic/cplustest/Cat");
    if (cls_cat == NULL) {
        return;
    }

    // 2.获取Cat的构造方法ID，构造方法的名统一为：<init>
    mid_cat_init = (*env).GetMethodID(cls_cat, "<init>", "(Ljava/lang/String;)V");
    if (mid_cat_init == NULL) {
        return;
    }

    // 3.创建一个String对象，作为构造方法的参数
    c_str_name = (*env).NewStringUTF("汤姆猫");
    if (c_str_name == NULL) {
        return;
    }

    // 4.创建Cat对象的实例，调用对象的构造方法并初始化对象
    obj_cat = (*env).NewObject(cls_cat, mid_cat_init, c_str_name);
    if (obj_cat == NULL) {
        return;
    }

    // -------------5.调用Cat父类Animal的runhegetName方法
    cls_animal = (*env).FindClass("com/avic/cplustest/Animal");
    if (cls_animal == NULL) {
        return;
    }

    //例1:调用父类的run方法
    mid_run = (*env).GetMethodID(cls_animal, "run", "()V");
    if (mid_run == NULL) {
        return;
    }

    // 注意：obj_cat是Cat的实例，cls_animal是Animal的Class引用，mid_run是Animal类中的方法ID
    (*env).CallNonvirtualVoidMethod(obj_cat, cls_animal, mid_run);

    // 例2：调用父类的getName方法
    //获取父类Animal中getName方法id
    mid_getName = (*env).GetMethodID(cls_animal, "getName", "()Ljava/lang/String;");
    if (mid_getName == NULL) {
        return;
    }

    (*env).CallNonvirtualObjectMethod(obj_cat, cls_animal, mid_getName);
    name = (*env).GetStringUTFChars(c_str_name, NULL);
    printf("In C: Animal Name is %s\n", name);

    // 释放从java层获取到的字符串所分配的内存
    (*env).ReleaseStringUTFChars(c_str_name, name);

    // 删除局部引用（jobject或jobject的子类才属于引用变量），允许VM释放被局部变量所引用的资源
    (*env).DeleteLocalRef(cls_cat);
    (*env).DeleteLocalRef(cls_animal);
    (*env).DeleteLocalRef(c_str_name);
    (*env).DeleteLocalRef(obj_cat);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_avic_cplustest_MainActivity_accessInstanceField(JNIEnv *env, jclass type, jobject obj) {

    // TODO
    jclass clazz;
    jfieldID fid;
    jstring j_str;
    jstring j_newStr;
    const char *c_str = NULL;

    // 1.获取AccessField类的Class引用
    clazz = (*env).GetObjectClass(obj);
    if (clazz == NULL) {
        return;;
    }

    // 2.获取AccessField类实例变量str属性ID
    fid = (*env).GetFieldID(clazz, "str", "Ljava/lang/String;");
    if (fid == NULL) {
        return;
    }

    // 3. 获取实例变量str的值
    j_str = (jstring) (*env).GetObjectField(obj, fid);

    // 4.将unicode编码的java字符串转换成C风格字符串
    c_str = (*env).GetStringUTFChars(j_str, NULL);

    if (c_str == NULL) {
        return;
    }
    printf("In C-->ClassField.str = %s\n", c_str);
    (*env).ReleaseStringUTFChars(j_str, c_str);

    // 5.修改实例变量str的值
    j_newStr = (*env).NewStringUTF("This is C String");
    if (j_newStr == NULL) {
        return;
    }

    (*env).SetObjectField(obj, fid, j_newStr);

    // 6.删除局部引用
    (*env).DeleteLocalRef(clazz);
    (*env).DeleteLocalRef(j_str);
    (*env).DeleteLocalRef(j_newStr);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_avic_cplustest_MainActivity_accessStaticField(JNIEnv *env, jclass type) {

    jclass clazz;
    jfieldID fid;
    jint num;

    // 1.获取ClassField类的Class引用
    clazz = (*env).FindClass("com/avic/cplustest/ClassField");
    if (clazz == NULL) {//错误处理
        return;
    }

    // 2.获取ClassField类静态变量num的属性ID
    fid = (*env).GetStaticFieldID(clazz, "num", "I");
    if (fid == NULL) {
        return;
    }

    // 3.获取静态变量num的值
    num = (*env).GetStaticIntField(clazz, fid);
    printf("In C---->ClassField.num = %d\n", num);

    // 4.修改静态变量num的值
    (*env).SetStaticIntField(clazz, fid, 80);

    //删除属性引用
    (*env).DeleteLocalRef(clazz);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_avic_cplustest_MainActivity_callJavaStaticMethod(JNIEnv *env, jclass type) {

    // TODO
    jclass clazz = NULL;
    jstring str_arg = NULL;
    jmethodID mid_static_method;
    //1.从classpath路径下搜索ClassMethod这个类，并返回该类的Class对象
    clazz = (*env).FindClass("com/avic/cplustest/ClassMethod");
    if (clazz == NULL) {
        return;
    }

    // 2.从clazz中查找callStaticMethod方法
    mid_static_method = (*env).GetStaticMethodID(clazz, "callStaticMethod",
                                                 "(Ljava/lang/String;I)V");
    if (mid_static_method == NULL) {
        printf("找不到callStaticMethod这个静态方法");
        return;
    }

    //3.调用clazz类的callStaticMethod方法
    str_arg = (*env).NewStringUTF("我是静态方法");
    (*env).CallStaticVoidMethod(clazz, mid_static_method, str_arg, 100);

    //删除局部引用
    (*env).DeleteLocalRef(clazz);
    (*env).DeleteLocalRef(str_arg);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_avic_cplustest_MainActivity_callJavaInstanceMethod(JNIEnv *env, jclass type) {

    // TODO
    jclass clazz = NULL;
    jobject jobj = NULL;
    jmethodID mid_construct = NULL;
    jmethodID mid_instance = NULL;
    jstring str_arg = NULL;
    // 1、从classpath路径下搜索ClassMethod这个类，并返回该类的Class对象
    clazz = (*env).FindClass("com/avic/cplustest/ClassMethod");
    if (clazz == NULL) {
        printf("找不到'com/avic/cplustest/ClassMethod这个类");
        return;
    }

    // 2、获取类的默认构造方法ID
    mid_construct = (*env).GetMethodID(clazz, "<init>", "()V");
    if (mid_construct == NULL) {
        printf("找不到默认的构造方法");
        return;
    }

    // 3、查找实例方法的ID
    mid_instance = (*env).GetMethodID(clazz, "callInstanceMethod", "(Ljava/lang/String;I)V");
    if (mid_instance == NULL) {

        return;
    }

    // 4、创建该类的实例
    jobj = (*env).NewObject(clazz, mid_construct);
    if (jobj == NULL) {
        printf("在com.study.jnilearn.ClassMethod类中找不到callInstanceMethod方法");
        return;
    }

    // 5、调用对象的实例方法
    str_arg = (*env).NewStringUTF("我是实例方法");
    (*env).CallVoidMethod(jobj, mid_instance, str_arg, 200);

    // 删除局部引用
    (*env).DeleteLocalRef(clazz);
    (*env).DeleteLocalRef(jobj);
    (*env).DeleteLocalRef(str_arg);


}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_avic_cplustest_MainActivity_helloWang(JNIEnv *env, jobject instance) {

    // TODO
    std::string wang = "this is from c++";

    return env->NewStringUTF(wang.c_str());
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_avic_cplustest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

